using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using System.Collections.Generic;
using Evolution.Utilities;
using System.Text;
using Evolution.Security;
using Evolution.Expression;

namespace Evolution.UI.WebParts
{   
    [ToolboxData("<{0}:PageActionWebPart runat=server></{0}:PageActionWebPart>"),
    Guid("B295178D-613A-4f45-98F9-118D050D0C14"), 
    XmlRoot(Namespace = "PageActionWebPart")]
    public class PageActionWebPart : System.Web.UI.WebControls.WebParts.WebPart
    {
        #region Fields

        private BaseAction _action;
        private Condition _condition;
        private SecurityEntity _owners;
        private ErrorNotificationMode _errorNotificationMode;
        private ActionList _supportedActions;
        private AssertionList _supportedAssertions;

        #endregion

        #region Contructors

        public PageActionWebPart()
        {
            if (this.Title.Length == 0)
                this.Title = "PageAction Part";
        }

        #endregion

        #region Properties

        public override object WebBrowsableObject
        {
            get { return this; }
        }

        public override PartChromeType ChromeType
        {
            get { return PartChromeType.None; }
            set { }
        }

        #region "Allow..." properties

        public override bool AllowEdit
        {
            get { return false; }
            set { }
        }

        public override bool AllowHide
        {
            get { return false; }
            set { }
        }

        public override bool AllowConnect
        {
            get { return false; }
            set { }
        }

        public override bool AllowClose
        {
            get 
            { 
                if(this.Owners.IsEmpty)
                    return base.AllowClose;

                return this.Owners.CheckCurrentUser(SPContext.Current.Web);
            }
            set { }
        }
       
        public override bool AllowMinimize
        {
            get 
            {
                if (this.Owners.IsEmpty)
                    return base.AllowMinimize; 

                return this.Owners.CheckCurrentUser(SPContext.Current.Web);                
            }
            set { }
        }

        public override bool AllowZoneChange
        {
            get
            {
                if (this.Owners.IsEmpty)
                    return base.AllowZoneChange;

                return this.Owners.CheckCurrentUser(SPContext.Current.Web);                                
            }
            set { }
        }

        #endregion

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorage(Storage.Shared)]
        public BaseAction Action
        {
            get
            {
                if (this._action == null)
                {
                    EmptyAction empty = new EmptyAction();
                    this._action = empty;
                }
                return this._action;
            }
            set 
            {
                if (value as BaseAction == null)
                    throw new ArgumentException();

                this._action = value; 
            }
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorage(Storage.Shared)]
        public Condition Condition
        {
            get
            {
                if (this._condition == null)
                    this._condition = new Condition();

                return this._condition;
            }
            set { this._condition = value; }
        }
                        
        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorage(Storage.Shared)]
        public SecurityEntity Owners
        {
            get
            {
                if (this._owners == null)
                    this._owners = SecurityEntity.Empty;

                return this._owners;
            }
            set { this._owners = value; }
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorage(Storage.Shared)]
        public ErrorNotificationMode ErrorNotificationMode
        {
            get { return this._errorNotificationMode; }
            set { this._errorNotificationMode = value; }
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorage(Storage.Shared)]
        public ActionList SupportedActions
        {
            get
            {
                if (this._supportedActions == null)
                    this._supportedActions = new ActionList();

                return this._supportedActions;
            }
            set { this._supportedActions = value; }
        }

        [WebBrowsable(false),
        Personalizable(PersonalizationScope.Shared),
        WebPartStorage(Storage.Shared)]
        public AssertionList SupportedAssertions
        {
            get
            {
                if (this._supportedAssertions == null)
                    this._supportedAssertions = new AssertionList();

                return this._supportedAssertions;
            }
            set { this._supportedAssertions = value; }
        }

        #endregion

        #region Methods

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            ClientScriptManager cs = this.Page.ClientScript;

            // Check to see if the include script exists already.
            if (!cs.IsClientScriptIncludeRegistered(this.GetType(), "PageActionJSScript"))
                cs.RegisterClientScriptInclude(this.GetType(), "PageActionJSScript", "/_layouts/Evolution/js/PageAction.js");
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            if (this.Parent.GetType().Equals(typeof(WebPartPreview)))
            {
                this.Controls.Add(new LiteralControl(this.GetWebPartContents("Presentation of the Page Action Web Part", "Use the <strong>Page Action Web Part</strong> to carry out actions on the Web page according to the satisfaction of a complex condition.", false, "pawp_normal.png")));
                return;
            }
           
            SPWebPartManager manager = (SPWebPartManager)SPWebPartManager.GetCurrentWebPartManager(this.Page);
            if (manager == null)
            {
                this.Controls.Add(new LiteralControl(@"<script type=""text/javascript"">alert(""The SPWebPartManager returned from GetCurrentWebPartManager method is null."");</script>"));
                return;
            }

            if (manager.DisplayMode == SPWebPartManager.BrowseDisplayMode)
            {
                if (this.ChromeState == PartChromeState.Normal)
                {
                    if (this.Action.RequireCondition && this.Condition.Count == 0)
                        this.AddErrorMessage(this.GetErrorContents(String.Format("Action exception: The action '{0}' requires the addition of a condition.", this.Action.Caption)));
                    else
                    {
                        bool result = false;
                        try
                        {
                            result = Condition.Evaluate(this.Condition, this.Page, this);
                            if (result)
                            {
                                try { this.Action.Execute(this.Page, this); }
                                catch (Exception actionException) { this.AddErrorMessage(this.GetErrorContents("Action exception: " + actionException.Message)); }
                            }
                        }
                        catch (Exception conditionException) { this.AddErrorMessage(this.GetErrorContents("Condition exception: " + conditionException.Message)); }
                    }
                }
            }
            else if (!this.Owners.IsEmpty && !this.Owners.CheckCurrentUser(SPContext.Current.Web))
            {
                this.Controls.Add(new LiteralControl(String.Format("<script type=\"text/javascript\">Evolution_Pawp_DisableMenu('{0}');</script>", this.ClientID)));
                this.Controls.Add(new LiteralControl(this.GetWebPartContents("Access limited to the owner", "You may not have permission to configure this Page Action Web Part. Contact the owners of this Web Part to find out if you have access permission.", false, "pawp_limited.png")));
            }
            else if ((this.Action as EmptyAction) != null)
                this.Controls.Add(new LiteralControl(this.GetWebPartContents("Configure the Page Action Web Part", "To configure this Page Action Web Part, open the tool pane, and then choose the <strong>Action</strong> and the <strong>Condition</strong>.", true, "pawp_normal.png")));
            else
            {
                string actionErrorMessage = String.Empty;
                int actionErrorCode = 0;
                
                if (this.Action.RequireCondition && this.Condition.Count == 0)
                    actionErrorCode = 1;
                else if (!this.Action.IsValidAction(this, out actionErrorMessage))
                    actionErrorCode = 2;

                Dictionary<int, string> conditionErrorMessages = new Dictionary<int, string>();
                for(int j = 0; j < this.Condition.Count; j++)
                {
                    BaseAssertion assertion = this.Condition[j].Assertion;

                    string conditionErrorMessage = String.Empty;
                    if (!assertion.IsValidAssertion(this, out conditionErrorMessage))
                        conditionErrorMessages.Add(j, "\"" + assertion.Caption + "\" exception: " + conditionErrorMessage);
                }

                if (actionErrorCode + conditionErrorMessages.Count != 0)
                    this.Controls.Add(new LiteralControl(this.GetWebPartContents("Page Action Web Part error", "Page Action Web Part error. Check in the panel below the nature of the error reported.", true, "pawp_error.png")));
                else
                    this.Controls.Add(new LiteralControl(this.GetWebPartContents("Page Action Web Part configured", "Page Action Web Part configured. To change your settings, open the tool pane, and then edit the <strong>Action</strong> and the <strong>Condition</strong>.", true, "pawp_configured.png")));

                switch (actionErrorCode)
                {
                    case 1:
                        this.Controls.Add(new LiteralControl(String.Format("<div class=\"UserGeneric\" style=\"color: red\"><img alt=\"Expand\" style=\"cursor: hand;\" src=\"/_layouts/images/plus.gif\" onclick=\"Evolution_Pawp_ToggleText(this, '{0}_Actions');\"  />&nbsp;<strong>Action exception</strong> <div id=\"{0}_Actions\" style=\"display: none; margin-left: 20px; margin-top: 5px\">The action '{1}' requires the addition of a condition.</div></div>", this.ClientID, this.Action.Caption)));
                        break;
                    case 2:
                        this.Controls.Add(new LiteralControl(String.Format("<div class=\"UserGeneric\" style=\"color: red\"><img alt=\"Expand\" style=\"cursor: hand;\" src=\"/_layouts/images/plus.gif\" onclick=\"Evolution_Pawp_ToggleText(this, '{0}_Actions');\"  />&nbsp;<strong>Action exception</strong> <div id=\"{0}_Actions\" style=\"display: none; margin-left: 20px; margin-top: 5px\">{1}</div></div>", this.ClientID, actionErrorMessage)));
                        break;
                    default:
                        this.Controls.Add(new LiteralControl(String.Format("<div class=\"UserGeneric\"><img alt=\"Expand\" style=\"cursor: hand;\" src=\"/_layouts/images/plus.gif\" onclick=\"Evolution_Pawp_ToggleText(this, '{0}_Actions');\"  />&nbsp;<strong>Action</strong> <div id=\"{0}_Actions\" style=\"display: none; margin-left: 20px; margin-top: 5px\">{1}</div></div>", this.ClientID, this.Action.GetDescription())));
                        break;
                }

                if (this.Condition.Count > 0)
                {
                    if (conditionErrorMessages.Count > 0)
                        this.Controls.Add(new LiteralControl(String.Format("<div class=\"UserGeneric\" style=\"color: red\"><img alt=\"Expand\" style=\"cursor: hand;\" src=\"/_layouts/images/plus.gif\" onclick=\"Evolution_Pawp_ToggleText(this, '{0}_Condition');\"  />&nbsp;<strong>Condition exception</strong> <div id=\"{0}_Condition\" style=\"display: none; margin-left: 20px; margin-top: 5px; color: black;\">", this.ClientID)));
                    else
                        this.Controls.Add(new LiteralControl(String.Format("<div class=\"UserGeneric\"><img alt=\"Expand\" style=\"cursor: hand;\" src=\"/_layouts/images/plus.gif\" onclick=\"Evolution_Pawp_ToggleText(this, '{0}_Condition');\"  />&nbsp;<strong>Condition</strong> <div id=\"{0}_Condition\" style=\"display: none; margin-left: 20px; margin-top: 5px; color: black;\">", this.ClientID)));

                    StringBuilder sb = new StringBuilder();
                    for (int j = 0; j < this.Condition.Count; j++)
                    {
                        AssertionElement e = this.Condition[j];

                        if (e.Operator != BooleanOperators.None)
                            sb.AppendFormat("<div style=\"margin-left: 20px; margin-top: 5px\">{0}</div>", e.Operator.ToString());

                        if (conditionErrorMessages.ContainsKey(j))
                            sb.AppendFormat("<div style=\"margin-top: 5px; color: red;\">{0}</div>", conditionErrorMessages[j]);
                        else
                            sb.AppendFormat("<div style=\"margin-top: 5px\">{0}</div>", e.Assertion.GetDescription());
                    }
                    this.Controls.Add(new LiteralControl(sb.ToString()));

                    this.Controls.Add(new LiteralControl("</div></div>"));
                }
            }
        }

        public override EditorPartCollection CreateEditorParts()
        {
            List<EditorPart> editors = new List<EditorPart>();
            EditorPart part = new PageActionEditor(this.ID);
            part.ID = this.ID + "_PageActionEditor";
            editors.Add(part);
            EditorPartCollection baseParts = base.CreateEditorParts();
            return new EditorPartCollection(baseParts, editors);
        }         

        protected void AddErrorMessage(string errorMessage)
        {
            switch (this.ErrorNotificationMode)
            {
                case ErrorNotificationMode.Always:
                    this.Controls.Add(new LiteralControl(errorMessage));
                    break;
                case ErrorNotificationMode.OnlyToSiteAdmins:
                    if (SPContext.Current.Web.CurrentUser.IsSiteAdmin)
                        this.Controls.Add(new LiteralControl(errorMessage));
                    break;
                case ErrorNotificationMode.OnlyToOwners:
                    if (!this.Owners.IsEmpty && this.Owners.CheckCurrentUser(SPContext.Current.Web))
                        this.Controls.Add(new LiteralControl(errorMessage));
                    break;
                case ErrorNotificationMode.BothSiteAdminsAndOwners:
                    if (!this.Owners.IsEmpty && this.Owners.CheckCurrentUser(SPContext.Current.Web) || SPContext.Current.Web.CurrentUser.IsSiteAdmin)
                        this.Controls.Add(new LiteralControl(errorMessage));
                    break;
            }
        }

        private string GetErrorContents(string message)
        {
            return String.Format("<span style=\"color: red;\">{0}</span>", message);
        }        

        private string GetWebPartContents(string title, string message, bool showOpenToolPane, string logoImage)
        {
            string contents = String.Format(@"
                <TABLE style=""WIDTH: 100%; HEIGHT: 100%"" border=""0"" cellSpacing=""0"" cellPadding=""0"">
                    <TBODY>
                        <TR>
                            <TD vAlign=""top"" noWrap>
                                <TABLE cellSpacing=""0"" cellPadding=""0"" border=""0"" width=""100%"">
                                    <TBODY>
                                        <TR bgcolor=""#FFE9A9"">
                                            <TD vAlign=""top"">&nbsp; </TD>
                                        </TR>
                                        <TR bgcolor=""#FFE9A9"">
                                            <TD>
                                                <div style=""font-size: 16pt; margin: 0px 0px 10px 5px; color: #F2CE6A; font-family: verdana"">{0}</div>
                                            </TD>
                                        </TR>
                                    </TBODY>
                                </TABLE>
                            </TD>
                        </TR>
                        <TR>
                            <TD style=""HEIGHT: 2%"" vAlign=""top"" width=""100%"">
                                <TABLE cellSpacing=""0"" cellPadding=""0"" width=""100%"" border=""0"">
                                    <TBODY>
                                        <TR>
                                            <TD>
                                                <TABLE cellSpacing=""0"" cellPadding=""0"">
                                                    <TBODY>
                                                        <TR>
                                                            <TD vAlign=""middle"" align=""center"">
                                                                <TABLE cellSpacing=""4"" cellPadding=""4"">
                                                                    <TBODY>
                                                                        <TR>
                                                                            <TD>
                                                                                <IMG alt=""Page Action Web Part"" src=""/_layouts/images/Evolution/{3}"" text=""Page Action Web Part"" /> 
                                                                            </TD>
                                                                            <TD><span style=""padding-right: 10px"">{1}<BR /><BR />{2}</span></TD>
                                                                        </TR>
                                                                    </TBODY>
                                                                </TABLE>
                                                            </TD>
                                                        </TR>
                                                    </TBODY>
                                                </TABLE>
                                            </TD>
                                        </TR>
                                    </TBODY>
                                </TABLE>
                            </TD>
                        </TR>
                        <TR>
                            <TD height=""1"" bgcolor=""#d9d9d9""><IMG height=""1"" alt="""" src=""/_layouts/images/blank.gif"" width=""1"" /></TD>
                        <TR>
                    </TBODY>
                </TABLE>", StringUtility.IsNullOrEmpty(title) ? "&nbsp;" : title, message, showOpenToolPane ? String.Format(@"<A href=""javascript:MSOTlPn_ShowToolPaneWrapper('{0}','{1}','{2}');"">Click here to open the tool pane.</a>", 1, 129, this.ID) : "", logoImage);

            return contents;
        }

        public static ExpressionInterpreter GetInterpreter(Page page, PageActionWebPart part)
        {
            ExpressionInterpreter interpreter = new ExpressionInterpreter(SPContext.Current.Web, page);

            interpreter.AddObject("CurrentWebPart", part);            

            return interpreter;
        }

        #endregion
    }

    #region ErrorNotificationMode enum

    public enum ErrorNotificationMode
    {
        Never = 0,
        Always = 1,
        OnlyToSiteAdmins = 2,
        OnlyToOwners = 3,
        BothSiteAdminsAndOwners = 4
    }

    #endregion
}
